package drds.plus.repository.mysql.execute_plan_to_sql;

import drds.plus.common.jdbc.SetParameterMethod;
import drds.plus.common.jdbc.SetParameterMethodAndArgs;
import drds.plus.repository.mysql.function.FunctionStringConstructor;
import drds.plus.repository.mysql.function.FunctionStringConstructorManager;
import drds.plus.sql_process.abstract_syntax_tree.IExecutePlanVisitor;
import drds.plus.sql_process.abstract_syntax_tree.Visitable;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.ExecutePlan;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.dml.Delete;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.dml.Insert;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.dml.Replace;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.dml.Update;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.QueryWithIndex;
import drds.plus.sql_process.abstract_syntax_tree.expression.NullValue;
import drds.plus.sql_process.abstract_syntax_tree.expression.bind_value.BindValue;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.column.Column;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.*;
import drds.plus.sql_process.abstract_syntax_tree.expression.order_by.OrderBy;
import drds.plus.sql_process.utils.DnfFilters;
import drds.tools.$;

import java.sql.Types;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutePlanVisitor implements IExecutePlanVisitor {

    private static Set<String> middleFunctionName = new HashSet<String>();

    static {
        middleFunctionName.add("+");
        middleFunctionName.add("-");
        middleFunctionName.add("*");
        middleFunctionName.add("/");
        middleFunctionName.add("%");
        //
        middleFunctionName.add("<");
        middleFunctionName.add("<=");
        middleFunctionName.add(">");
        middleFunctionName.add(">=");
        middleFunctionName.add("=");
        middleFunctionName.add("<=>");
        middleFunctionName.add("!=");
        //
        middleFunctionName.add("and");
        middleFunctionName.add("or");
        //
        middleFunctionName.add("constant");
        middleFunctionName.add("is");
        middleFunctionName.add("like");
        middleFunctionName.add("in");

    }

    protected boolean bindValue = true;
    protected Map<Integer, SetParameterMethodAndArgs> outPutIndexToSetParameterMethodAndArgsMap;
    protected Map<Integer, SetParameterMethodAndArgs> inPutIndexToSetParameterMethodAndArgsMap;
    protected Map<Integer, Integer> newParamIndexToOldMap = null;
    protected AtomicInteger bindValueSequence;
    protected StringBuilder sql = new StringBuilder();
    protected FunctionStringConstructorManager functionStringConstructorManager = new FunctionStringConstructorManager();
    protected ExecutePlan executePlan;
    protected boolean isGroupBy = false;

    public ExecutePlanVisitor(ExecutePlan executePlan, Map<Integer, SetParameterMethodAndArgs> inputParamMap, Map<Integer, SetParameterMethodAndArgs> outputParamMap, Map<Integer, Integer> newParamIndexToOldMap, AtomicInteger bindValueSequence, boolean bindValue) {
        this(executePlan, inputParamMap, outputParamMap, newParamIndexToOldMap, bindValueSequence, bindValue, false);
    }

    public ExecutePlanVisitor(ExecutePlan executePlan, Map<Integer, SetParameterMethodAndArgs> inputParamMap, Map<Integer, SetParameterMethodAndArgs> outputParamMap, Map<Integer, Integer> newParamIndexToOldMap, AtomicInteger bindValueSequence, boolean bindValue, boolean isGroupBy) {
        this.executePlan = executePlan;
        this.inPutIndexToSetParameterMethodAndArgsMap = inputParamMap;

        this.outPutIndexToSetParameterMethodAndArgsMap = outputParamMap;

        if (this.outPutIndexToSetParameterMethodAndArgsMap == null) {
            this.outPutIndexToSetParameterMethodAndArgsMap = new HashMap<Integer, SetParameterMethodAndArgs>();
        }

        this.newParamIndexToOldMap = newParamIndexToOldMap;

        if (this.newParamIndexToOldMap == null) {
            this.newParamIndexToOldMap = new HashMap();
        }

        if (bindValueSequence != null) {
            this.bindValueSequence = bindValueSequence;
        } else {
            this.bindValueSequence = new AtomicInteger(1);
        }

        this.bindValue = bindValue;
        this.isGroupBy = isGroupBy;
    }

    protected void buildGroupBy(Query query) {
        boolean first = true;
        if (query.getGroupByList() != null && !query.getGroupByList().isEmpty()) {
            sql.append(" group by ");
            first = true;
            List<OrderBy> orderByList = query.getGroupByList();
            for (OrderBy orderBy : orderByList) {
                if (first) {
                    first = false;
                } else {
                    sql.append(",");
                }
                ExecutePlanVisitor executePlanVisitor = this.getOrderbyVisitor(orderBy, true);
                sql.append(executePlanVisitor.getString());
            }
        }
    }

    protected void buildHaving(Query query) {
        if (query.getHaving() != null) {
            sql.append(" having ");
            ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(query.getHaving());
            sql.append(executePlanVisitor.getString());
        }
    }

    protected void buildLimit(Query query) {
        Long limitFrom = (Long) query.getLimitFrom();
        Long limitTo = (Long) query.getLimitTo();
        if ((limitFrom == null || limitFrom == -1) && (limitTo == null || limitTo == -1)) {
            return;
        }
        sql.append(" limit ");
        ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(limitFrom);
        sql.append(executePlanVisitor.getString());
        if (limitTo != null && limitTo != -1) {
            executePlanVisitor = this.getNewExecutePlanVisitor(limitTo);
            sql.append(",").append(executePlanVisitor.getString());
        }
    }

    protected void buildOrderBy(Query<Query> queryTreeQuery) {
        boolean first = true;
        if (queryTreeQuery.getOrderByList() != null && !queryTreeQuery.getOrderByList().isEmpty()) {
            sql.append(" order by ");
            first = true;
            for (OrderBy orderBy : queryTreeQuery.getOrderByList()) {
                if (first) {
                    first = false;
                } else {
                    sql.append(",");
                }

                ExecutePlanVisitor executePlanVisitor = this.getOrderbyVisitor(orderBy, false);
                sql.append(executePlanVisitor.getString());
            }
        }
    }

    public void buildSelect(Query query) {
        sql.append("query ");
        boolean hasDistinct = false;
        boolean first = true;
        StringBuilder sb = new StringBuilder();
        List<Item> itemList = query.getItemList();
        for (Item item : itemList) {
            if (first) {
                first = false;
            } else {
                sb.append(",");
            }
            if (item.isDistinct()) {
                hasDistinct = true;
            }
            ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(item);
            sb.append(executePlanVisitor.getString());
            if (item.getAlias() != null) {
                sb.append(" as ").append(item.getAlias());
            }
        }

        if (hasDistinct) {
            sql.append(" distinct ");
        }

        sql.append(sb);
    }

    public ExecutePlanVisitor getOrderbyVisitor(OrderBy orderBy, boolean isGroupBy) {
        ExecutePlanVisitor executePlanVisitor = new ExecutePlanVisitor(executePlan, inPutIndexToSetParameterMethodAndArgsMap, outPutIndexToSetParameterMethodAndArgsMap, newParamIndexToOldMap, bindValueSequence, bindValue, isGroupBy);

        if (orderBy instanceof Visitable) {
            orderBy.accept(executePlanVisitor);
        } else {
            executePlanVisitor.visit(orderBy);
        }

        return executePlanVisitor;
    }

    public ExecutePlanVisitor getNewExecutePlanVisitor(Object o) {
        ExecutePlanVisitor executePlanVisitor = new ExecutePlanVisitor(executePlan, inPutIndexToSetParameterMethodAndArgsMap, outPutIndexToSetParameterMethodAndArgsMap, newParamIndexToOldMap, bindValueSequence, bindValue, false);

        if (o instanceof Visitable) {
            ((Visitable) o).accept(executePlanVisitor);
        } else {
            executePlanVisitor.visit(o);
        }

        return executePlanVisitor;
    }

    public ExecutePlanVisitor getNewExecutePlanVisitor(Query query, Object o) {
        ExecutePlanVisitor executePlanVisitor = new ExecutePlanVisitor(query, inPutIndexToSetParameterMethodAndArgsMap, outPutIndexToSetParameterMethodAndArgsMap, newParamIndexToOldMap, bindValueSequence, bindValue, false);

        if (o instanceof Visitable) {
            ((Visitable) o).accept(executePlanVisitor);
        } else {
            executePlanVisitor.visit(o);
        }

        return executePlanVisitor;
    }

    public Map<Integer, SetParameterMethodAndArgs> getOutPutIndexToSetParameterMethodAndArgsMap() {
        return outPutIndexToSetParameterMethodAndArgsMap;
    }

    public Map<Integer, Integer> getNewParamIndexToOldMap() {
        return newParamIndexToOldMap;
    }

    public String getString() {
        return sql.toString();
    }

    private boolean isMiddleFunction(Function function) {
        if (middleFunctionName.contains(function.getFunctionName())) {
            return function.getArgList() == null || function.getArgList().size() != 1;
        } else {
            return false;
        }
    }

    public void setParamMap(Map<Integer, SetParameterMethodAndArgs> paramMap) {
        this.outPutIndexToSetParameterMethodAndArgsMap = paramMap;
    }

    public void visit(Column column) {
        // 别名加在select之外，如(selectStatement * from where) setAliasAndSetNeedBuild
        // t1,列名之前不能使用这个别名
        // 别名加在select之内，如select * from where setAliasAndSetNeedBuild t1，列名之前可以使用这个别名
        if (executePlan instanceof Query && !((Query) executePlan).isSubQuery() && ((Query) executePlan).getAlias() != null && column.getTableName() != null) {
            sql.append(((Query) executePlan).getAlias());
        } else {
            if (executePlan instanceof QueryWithIndex && column.getTableName() != null) {
                sql.append(((QueryWithIndex) executePlan).getTableName());
            } else {
                if (executePlan instanceof Query && column.getTableName() != null) {
                    sql.append(column.getTableName());
                } else {
                    sql.append(column.getColumnName());
                    return;
                }
            }
        }

        sql.append(".");
        sql.append(column.getColumnName());
    }

    public void visit(Filter filter) {
        visit((Function) filter);
    }

    public void visit(Function function) {
        if (function.isNot()) {
            sql.append("(");
            sql.append(" NOT ");
        }
        String functionName = function.getFunctionName();
        FunctionStringConstructor functionStringConstructor = functionStringConstructorManager.getConstructor(function);
        if (functionStringConstructor != null) {
            sql.append(functionStringConstructor.constructColumnNameForFunction(executePlan, bindValue, bindValueSequence, outPutIndexToSetParameterMethodAndArgsMap, function, this));
        } else {
            boolean isMiddle = isMiddleFunction(function);
            if (isMiddle) {
                sql.append("(");
                if (function instanceof Filter) {
                    functionName = ((Filter) function).getOperation().getOperationString();
                }
            }
            if ((function instanceof Filter) && Operation.constant.equals(((Filter) function).getOperation())) {
                ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(function.getArgList().get(0));
                sql.append(executePlanVisitor.getString());// 常量，不太可能走到这一步
            } else if ((function instanceof BooleanFilter) && (//
                    Operation.is_null.equals(((BooleanFilter) function).getOperation()) || //
                            Operation.is_not_null.equals(((BooleanFilter) function).getOperation()) ||//
                            Operation.is_true.equals(((BooleanFilter) function).getOperation()) ||//
                            Operation.is_not_true.equals(((BooleanFilter) function).getOperation()) || //
                            Operation.is_false.equals(((BooleanFilter) function).getOperation()) || //
                            Operation.is_not_false.equals(((BooleanFilter) function).getOperation())//

            )) {
                ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(function.getArgList().get(0));
                sql.append(executePlanVisitor.getString());
                sql.append(" ").append(functionName);
            } else {
                if (!isMiddle) {
                    if (FunctionName.minus.equals(functionName)) {
                        sql.append("-");
                    } else if (!FunctionName.row.equals(functionName)) { // row代表向量匹配
                        sql.append(functionName);
                    }

                    sql.append("(");
                }
                boolean first = true;
                boolean isDistinct = false;
                StringBuilder args = new StringBuilder();
                for (Object arg : function.getArgList()) {
                    if (first) {
                        first = false;
                    } else if (isMiddle) {
                        args.append(" ").append(functionName).append(" ");
                    } else {
                        args.append(",");
                    }

                    if (arg instanceof Item && ((Item) arg).isDistinct()) {
                        isDistinct = true;
                    }
                    ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(arg);
                    args.append(executePlanVisitor.getString());
                }
                if (isDistinct) {
                    sql.append(" distinct ");
                }
                sql.append(args);
                if (!isMiddle) {
                    sql.append(")");
                }
            }

            if (isMiddle) {
                sql.append(")");
            }
        }
        if (function.isNot()) {
            sql.append(")");
        }

    }

    public void visit(Join join) {
        if (join.isSubQuery() && !join.isTopQuery()) {
            sql.append(" ( ");
        }

        if (join.isSubQuery() || join.isTopQuery()) {
            buildSelect(join);
            sql.append(" from ");
        }

        Query leftNode = join.getLeftNode();
        Query rightNode = join.getRightNode();

        ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(leftNode, leftNode);
        sql.append(executePlanVisitor.getString());
        if (join.isLeftOuterJoin() && join.isRightOuterJoin()) {
            throw new RuntimeException("full outter join 不支持");
        } else if (join.isLeftOuterJoin() && !join.isRightOuterJoin()) {
            sql.append(" left");
        } else if (join.isRightOuterJoin() && !join.isLeftOuterJoin()) {
            sql.append(" right");
        }
        sql.append(" join ");
        executePlanVisitor = this.getNewExecutePlanVisitor(rightNode, rightNode);
        sql.append(executePlanVisitor.getString());
        sql.append(" on ");
        StringBuilder joinOnFilterStr = new StringBuilder();
        boolean first = true;
        for (int i = 0; i < join.getLeftJoinColumnList().size(); i++) {
            if (first) {
                first = false;
            } else {
                joinOnFilterStr.append(" and ");
            }
            Item leftColumn = join.getLeftJoinColumnList().get(i);
            Item rightColumn = join.getRightJoinColumnList().get(i);
            joinOnFilterStr.append(this.getNewExecutePlanVisitor(leftColumn).getString());
            joinOnFilterStr.append(" = ");
            joinOnFilterStr.append(this.getNewExecutePlanVisitor(rightColumn).getString());
        }

        if (join.getOtherJoinOnFilter() != null) {
            if (first) {
                first = false;
            } else {
                joinOnFilterStr.append(" and ");
            }

            joinOnFilterStr.append(this.getNewExecutePlanVisitor(join.getOtherJoinOnFilter()).getString());
        }

        sql.append(joinOnFilterStr.toString());
        if (join.isSubQuery() || join.isTopQuery()) {
            String whereFilterStr = "";

            if (join.getWhereFilter() != null) {
                executePlanVisitor = this.getNewExecutePlanVisitor(join.getWhereFilter());
                whereFilterStr = executePlanVisitor.getString();
            }

            if ($.isNotNullAndNotEmpty(whereFilterStr)) {
                sql.append(" where ");
                sql.append(whereFilterStr);
            }
            buildGroupBy(join);
            buildHaving(join);
            buildOrderBy(join);
            buildLimit(join);

            switch (join.getLockMode()) {
                case exclusive_lock:
                    sql.append(" for update");
                    break;
                case shared_lock:
                    sql.append(" lock in share mode");
                    break;
                default:
                    break;
            }
        }

        if (join.isSubQuery() && !join.isTopQuery()) {
            sql.append(" ) ");
            if (join.getAlias() != null)
                sql.append(" ").append(join.getAlias()).append(" ");
        }
    }

    public void visit(OrderBy orderBy) {
        ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(orderBy.getItem());
        sql.append(executePlanVisitor.getString());

        if (!isGroupBy) {
            if (orderBy.getAsc()) {
                sql.append(" asc ");
            } else {
                sql.append(" desc ");
            }
        }
    }

    public void visit(QueryWithIndex queryWithIndex) {
        if (queryWithIndex.isSubQuery() && !queryWithIndex.isTopQuery()) {
            sql.append(" ( ");
        }
        if (queryWithIndex.isSubQuery() || queryWithIndex.isTopQuery()) {
            buildSelect(queryWithIndex);

            if (queryWithIndex.getTableName() == null && queryWithIndex.getSubQuery() == null) {
                return;
            }
            sql.append(" from ");
        }

        if (queryWithIndex.getTableName() != null) {

            sql.append(queryWithIndex.getTableName());

            if (!queryWithIndex.isSubQuery() && queryWithIndex.getAlias() != null && !queryWithIndex.getAlias().equalsIgnoreCase(queryWithIndex.getTableName())) {
                sql.append(" ").append(queryWithIndex.getAlias());
            }
        } else if (queryWithIndex.getSubQuery() != null) {
            sql.append(this.getNewExecutePlanVisitor(queryWithIndex.getSubQuery(), queryWithIndex.getSubQuery()).getString());
        }

        if (queryWithIndex.isSubQuery() || queryWithIndex.isTopQuery()) {
            String keyFilterStr = "";

            Filter whereFilter = DnfFilters.and(DnfFilters.and(queryWithIndex.getKeyFilter(), queryWithIndex.getValueFilter()), queryWithIndex.getOtherJoinOnFilter());

            if (whereFilter != null) {
                ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(whereFilter);
                keyFilterStr = executePlanVisitor.getString();
            }

            if ($.isNotNullAndEmpty(keyFilterStr)) {
                sql.append(" where ");
                sql.append(keyFilterStr);
            }
            buildGroupBy(queryWithIndex);
            buildHaving(queryWithIndex);
            buildOrderBy(queryWithIndex);
            buildLimit(queryWithIndex);

            switch (queryWithIndex.getLockMode()) {
                case exclusive_lock:
                    sql.append(" for update");
                    break;
                case shared_lock:
                    sql.append(" lock in share mode");
                    break;
                default:
                    break;
            }
        }

        if (queryWithIndex.isSubQuery() && !queryWithIndex.isTopQuery()) {
            sql.append(" ) ");
            if (queryWithIndex.getAlias() != null) {
                sql.append(" ").append(queryWithIndex.getAlias()).append(" ");
            }
        }
    }

    public void visit(List list1) {
        List<Comparable> list = list1;
        sql.append("(");
        boolean first = true;
        for (Comparable comparable : list) {
            if (first) {
                first = false;
            } else {
                sql.append(",");
            }
            ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(comparable);
            sql.append(executePlanVisitor.getString());
        }
        sql.append(")");
    }

    public void visit(NullValue nullValue) {
        sql.append("null");
        return;
    }

    public void visit(Object o) {
        if (o instanceof List) {
            visit((List) o);
        } else {
            if (o instanceof Boolean) {
                sql.append(((Boolean) o).toString());
                return;
            }

            if (o instanceof NullValue) {
                sql.append("null");
                return;
            }

            int index = bindValueSequence.getAndIncrement();
            SetParameterMethodAndArgs setParameterMethodAndArgs = null;
            if (o != null && !(o instanceof NullValue)) {
                setParameterMethodAndArgs = new SetParameterMethodAndArgs(SetParameterMethod.setObject, new Object[]{index, o});
            } else {
                setParameterMethodAndArgs = new SetParameterMethodAndArgs(SetParameterMethod.setNull1, new Object[]{index, Types.NULL});
            }
            this.outPutIndexToSetParameterMethodAndArgsMap.put(index, setParameterMethodAndArgs);
            sql.append("?");
        }

    }

    public void visit(BindValue bindValue) {
        int index = bindValueSequence.getAndIncrement();
        SetParameterMethodAndArgs setParameterMethodAndArgs = null;

        setParameterMethodAndArgs = new SetParameterMethodAndArgs(inPutIndexToSetParameterMethodAndArgsMap.get(bindValue.getIndex()).getSetParameterMethod(), new Object[]{index, inPutIndexToSetParameterMethodAndArgsMap.get(bindValue.getIndex()).getArgs()[1]});

        this.newParamIndexToOldMap.put(index, bindValue.getIndex());

        this.outPutIndexToSetParameterMethodAndArgsMap.put(index, setParameterMethodAndArgs);
        sql.append("?");

    }

    public void visit(Insert insert) {
        sql.append("insert ");
        sql.append("into ");

        sql.append(insert.getTableName()).append(" ");

        boolean first = true;
        if ($.isNotNullAndHasElement(insert.getUpdateItemList())) {
            sql.append("( ");
            for (int i = 0; i < insert.getUpdateItemList().size(); i++) {
                if (first) {
                    first = false;
                } else {
                    sql.append(", ");
                }
                sql.append(this.getNewExecutePlanVisitor(insert.getUpdateItemList().get(i)).getString());
            }
            sql.append(") ");
        }

        if (insert.getQuery() != null) {
            insert.getQuery().setTopQuery(true);
            ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(insert.getQuery(), insert.getQuery());
            sql.append(executePlanVisitor.getString());
        } else {
            sql.append("valueList ");
            for (int valuesIndex = 0; valuesIndex < insert.getValueListListSize(); valuesIndex++) {
                if (valuesIndex == 0) {
                    sql.append("( ");
                } else {
                    sql.append(", ( ");
                }

                first = true;
                List<Object> valueList = insert.getValueList(valuesIndex);
                for (int i = 0; i < valueList.size(); i++) {
                    if (first) {
                        first = false;
                    } else {
                        sql.append(", ");
                    }
                    sql.append(this.getNewExecutePlanVisitor(valueList.get(i)).getString());
                }
                sql.append(") ");
            }
        }

        if (insert.getDuplicateUpdateColumns() != null && !insert.getDuplicateUpdateColumns().isEmpty()) {
            sql.append("on duplicate columnName update ");
            first = true;
            for (int i = 0; i < insert.getDuplicateUpdateColumns().size(); i++) {
                if (first) {
                    first = false;
                } else {
                    sql.append(", ");
                }

                sql.append(this.getNewExecutePlanVisitor(insert.getDuplicateUpdateColumns().get(i)).getString());
                sql.append(" = ");
                sql.append(this.getNewExecutePlanVisitor(insert.getDuplicateUpdateValues().get(i)).getString());
            }

            sql.append(" ");
        }
    }

    public void visit(Replace replace) {
        sql.append("replace ");
        sql.append("into ");

        sql.append(replace.getTableName()).append(" ");
        boolean first = true;
        if ($.isNotNullAndHasElement(replace.getUpdateItemList())) {
            sql.append("( ");
            for (int i = 0; i < replace.getUpdateItemList().size(); i++) {
                if (first) {
                    first = false;
                } else {
                    sql.append(", ");
                }
                sql.append(this.getNewExecutePlanVisitor(replace.getUpdateItemList().get(i)).getString());
            }
            sql.append(") ");
        }

        sql.append("valueList ");
        for (int valuesIndex = 0; valuesIndex < replace.getValueListListSize(); valuesIndex++) {

            if (valuesIndex == 0) {
                sql.append("( ");
            } else {
                sql.append(", ( ");
            }

            first = true;

            List<Object> valueList = replace.getValueList(valuesIndex);
            for (int i = 0; i < valueList.size(); i++) {
                if (first) {
                    first = false;
                } else {
                    sql.append(", ");
                }
                sql.append(this.getNewExecutePlanVisitor(valueList.get(i)).getString());
            }
            sql.append(") ");
        }

    }

    public void visit(Update update) {
        sql.append("update ");

        sql.append(update.getTableName()).append(" ");

        sql.append("setLimitValue ");
        boolean first = true;

        if (update.getUpdateItemList() != null && !update.getUpdateValues().isEmpty()) {
            for (int i = 0; i < update.getUpdateItemList().size(); i++) {
                if (first) {
                    first = false;
                } else {
                    sql.append(", ");
                }

                sql.append(this.getNewExecutePlanVisitor(update.getUpdateItemList().get(i)).getString());
                sql.append(" = ");
                sql.append(this.getNewExecutePlanVisitor(update.getUpdateValues().get(i)).getString());
            }
        }
        String keyFilterStr = "";
        String resultFilterStr = "";

        Query query = update.getQuery();
        if (query instanceof QueryWithIndex && ((QueryWithIndex) query).getKeyFilter() != null) {
            ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(((QueryWithIndex) query).getKeyFilter());
            keyFilterStr = executePlanVisitor.getString();
        }

        if (query.getValueFilter() != null) {
            ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(query.getValueFilter());
            resultFilterStr = executePlanVisitor.getString();
        }

        if ($.isNotNullAndNotEmpty(keyFilterStr) || $.isNotNullAndNotEmpty(resultFilterStr)) {
            sql.append(" where ");
            sql.append(keyFilterStr);
            if ($.isNotNullAndNotEmpty(keyFilterStr)) {
                if ($.isNotNullAndNotEmpty(resultFilterStr)) {
                    sql.append("and ");
                }

            }
            sql.append(resultFilterStr);
            buildLimit(query);
        }

    }

    public void visit(Delete delete) {
        sql.append("delete ");
        sql.append("from ");

        sql.append(delete.getTableName()).append(" ");

        String keyFilterStr = "";
        String resultFilterStr = "";

        Query query = delete.getQuery();
        if (query instanceof QueryWithIndex && ((QueryWithIndex) query).getKeyFilter() != null) {
            ExecutePlanVisitor executePlanVisitor = this.getNewExecutePlanVisitor(((QueryWithIndex) query).getKeyFilter());
            keyFilterStr = executePlanVisitor.getString();
        }

        if (query.getValueFilter() != null) {
            ExecutePlanVisitor visitor = this.getNewExecutePlanVisitor(query.getValueFilter());
            resultFilterStr = visitor.getString();
        }

        if ($.isNotNullAndNotEmpty(keyFilterStr) || $.isNotNullAndNotEmpty(resultFilterStr)) {
            sql.append(" where ");
            sql.append(keyFilterStr);
            if ($.isNotNullAndNotEmpty(keyFilterStr)) {
                if ($.isNotNullAndNotEmpty(resultFilterStr)) {
                    sql.append("and ");
                }

            }
            sql.append(resultFilterStr);
        }

        buildLimit(query);
    }

}
